package com.joker.aigc.chat.server.handler;

import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;

import com.joker.aigc.chat.domain.enums.WebSocketDtoTypeEnum;
import com.joker.aigc.chat.domain.entity.websocket.WebSocketAuthorize;
import com.joker.aigc.chat.domain.entity.websocket.WebSocketReq;
import com.joker.aigc.chat.service.SocketService;
import com.joker.aigc.utils.NettyUtil;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

/**
 * 继承 SimpleChannelInboundHandler 。我们只关注TextWebSocketFrame 类
 */
@Slf4j
@Sharable
public class NettyWebSocketServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    private SocketService socketService;

    // 当web客户端连接后，触发该方法
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("handlerAdded--{}", ctx);
        this.socketService = getService();
    }

    // 客户端离线
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        // 当客户端离线触发离线操作
        userOffLine(ctx);
    }

    /**
     * 取消绑定
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        // 可能出现业务判断离线后再次触发 channelInactive
        log.warn("触发 channelInactive 掉线![{}]", ctx.channel().id());
        userOffLine(ctx);
    }

    // 用户下线，就移除掉这个
    private void userOffLine(ChannelHandlerContext ctx) {
        this.socketService.removed(ctx.channel());
        ctx.channel().close();
    }

    /**
     * 心跳检查
     *
     * @param ctx
     * @param evt
     * @throws Exception
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        log.info("心跳检查--> userEventTriggered->{},->{}", ctx, evt);
        // 这里是心跳检查
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
            // 读空闲
            if (idleStateEvent.state() == IdleState.READER_IDLE) {
                // 关闭用户的连接
                userOffLine(ctx);
            }
        } else if (evt instanceof WebSocketServerProtocolHandler.HandshakeComplete) {
            this.socketService.connect(ctx.channel());
            // 获取到token信息
            String token = NettyUtil.getAttr(ctx.channel(), NettyUtil.TOKEN);
            // 如果用户登录成功
            if (StringUtils.isNotBlank(token)) {
                this.socketService.authorize(ctx.channel(), new WebSocketAuthorize(token));
            }
        }
        super.userEventTriggered(ctx, evt);
    }

    // 处理异常
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.warn("异常发生，异常消息 ={}", cause);
        ctx.channel().close();
    }

    private SocketService getService() {
        return SpringUtil.getBean(SocketService.class);
    }

    // 读取客户端发送的请求报文
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
        log.info("客户端发送请求 channelRead--{},TextWebSocketFrame: {}", ctx, msg);
        WebSocketReq wsBaseReq = JSONUtil.toBean(msg.text(), WebSocketReq.class);
        WebSocketDtoTypeEnum wsReqTypeEnum = WebSocketDtoTypeEnum.of(wsBaseReq.getType());
        switch (wsReqTypeEnum) {
            case LOGIN:
//                log.info("登录状态");
                // 如果是登录的话，则进行
//                this.webSocketService.handleLoginReq(ctx.channel());
//                WSBaseReq wsBaseResp=new WSBaseReq();
//                ctx.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(wsBaseResp)));
//                ctx.channel().writeAndFlush("asda");
//                log.info("请求二维码 = " + msg.text());
                break;
            case HEARTBEAT:
                log.info("心跳连接");
                break;
            default:
                log.info("未知类型");
        }
    }
}
